use chrono::prelude::*;
use std::collections::HashMap;
use std::hash::Hash;

pub fn md5(str: String) -> String {
    // TODO

    "123456789".to_owned()
}

pub fn get_date() -> (i16, i8, i8) {
    let datetime = Local::now();
    let date = datetime.date_naive();
    let year = date.year();
    let month = date.month();
    let day = date.day();
    (year as i16, month as i8, day as i8)
}

pub fn get_time() -> (i8, i8, i8) {
    let datetime = Local::now();
    let time = datetime.time();
    let hour = time.hour();
    let minute = time.minute();
    let second = time.second();
    (hour as i8, minute as i8, second as i8)
}

pub fn timestamp() -> i64 {
    let datetime = Local::now();
    datetime.timestamp()
}

pub fn format_datetime(datetime: &NaiveDateTime, fmt: &str) -> String {
    datetime.format(fmt).to_string()
}

pub fn format_date(datetime: &NaiveDate, fmt: &str) -> String {
    datetime.format(fmt).to_string()
}

pub fn day2date(day: i32) -> Option<NaiveDate> {
    let year = day / 10000;
    let month = ((day % 10000) / 100) as u32;
    let d = (day % 100) as u32;
    NaiveDate::from_ymd_opt(year, month, d)
}

pub fn datetime2day(datetime: &NaiveDateTime) -> i32 {
    date2day(&datetime.date())
}

pub fn date2day(date: &NaiveDate) -> i32 {
    // datetime.year() * 10000 + datetime.month() * 100 + datetime.day()
    let year = date.year();
    let month = date.month() as i32;
    let d = date.day() as i32;
    year * 10000 + month * 100 + d
}

pub fn str2datetime(datetime_str: &str, fmt: &str) -> Option<NaiveDateTime> {
    let res = NaiveDateTime::parse_from_str(datetime_str, fmt);
    if res.is_err() {
        //log!()
        // error!("parse {} to NaiveDateTime error. err: {:?} ",datetime_str, res.err() );
        return None;
    }
    return Some(res.ok().unwrap());
}

pub fn str2date(date_str: &str, fmt: &str) -> Option<NaiveDate> {
    let res = NaiveDate::parse_from_str(date_str, fmt);
    if res.is_err() {
        //log!()
        // error!("parse {} to NaiveDate error. err: {:?} ",date_str, res.err() );
        return None;
    }
    return Some(res.ok().unwrap());
}

pub fn str2time(time_str: &str, fmt: &str) -> Option<NaiveTime> {
    let res = NaiveTime::parse_from_str(time_str, fmt);
    if res.is_err() {
        //log!()
        // error!("parse {} to NaiveDate error. err: {:?} ",date_str, res.err() );
        return None;
    }
    return Some(res.ok().unwrap());
}

pub fn format_current_datetime(fmt: &str) -> String {
    let datetime = Local::now();
    format_datetime(&datetime.naive_local(), fmt)
}

pub fn get_native_time(hhmmss: u32) -> NaiveTime {
    let hour = hhmmss / 10000;
    let min = (hhmmss - hour * 10000) / 100;
    let sec = hhmmss % 100;
    NaiveTime::from_hms_opt(hour, min, sec).unwrap()
}

pub fn convert_list_to_index_map<A,T,F,I>(list:A,f:F) -> HashMap<I,T>
    where 
    A: IntoIterator<Item = T>,
    F: Fn(&T) -> I,
    I: Eq + Hash,
{
    let mut res = HashMap::new();
    list.into_iter().all(|item|{
        let key = f(&item);
        res.insert(key,item);
        true
    });
    res
}

#[inline]
pub fn number_in_range<T: PartialOrd+Ord+Copy>(val:T,min:T,max:T) -> T {
    let mut tmp = val;
    if tmp < min {
        tmp = min;
    }
    if tmp > max {
        tmp = max;
    }
    tmp
}

#[derive(Debug, Clone, Copy)]
pub struct UsedTime {
    pub first: NaiveTime,
    pub next: Option<NaiveTime>,
}

impl Default for UsedTime {
    fn default() -> Self {
        let now = Local::now();
        Self {
            first: now.time(),
            next: None,
        }
    }
}
impl UsedTime {
    pub fn next(&mut self) -> &mut Self {
        let now = Local::now();
        if self.next.is_none() {
            self.next = Some(now.time());
        } else {
            self.first = self.next.unwrap();
            self.next = Some(now.time());
        }
        self
    }

    pub fn used(&self) -> i64 {
        if self.next.is_none() {
            return 0;
        } else {
            let end = self.next.unwrap();
            let dur = end - self.first;
            return dur.num_milliseconds();
        }
    }

    pub fn used_str(&self) -> String {
        let dur = self.used();
        format!("{}.{:03}", dur / 1000, dur % 1000)
    }
}

#[test]
fn test_native_time_calc() {
    let t1 = NaiveTime::from_hms_opt(10, 22, 56).unwrap();
    let t2 = NaiveTime::from_hms_opt(23, 13, 7).unwrap();
    let sec = t2 - t1;
    println!("{:?}", sec.num_seconds())
}

#[test]
fn test_native_datetime() {
    let time = NaiveTime::from_hms_opt(12, 13, 56).unwrap();
    let date = day2date(20240712);
    let d = NaiveDateTime::new(date.unwrap(), time);    
    println!("{:?}", d);
    println!("day: {}",datetime2day(&d));
}



// #[derive(Debug,Default)]
// pub struct BaseInfo{
//     pub id:i32,
//     pub code: String,
//     pub name: String,
// }

// #[test]
// fn test_convert_list(){
//     let list = vec![
//         BaseInfo{
//             id:1,
//             code: "000001".to_owned(),
//             name: "shanghai".to_owned()
//         },
//         BaseInfo{
//             id:2,
//             code: "000002".to_owned(),
//             name: "shenzhen".to_owned()
//         },
//         BaseInfo{
//             id:3,
//             code: "000003".to_owned(),
//             name: "beijing".to_owned()
//         },
//     ];

//     // let index1 = convert_list_to_map(list, |item|{
//     //     item.id
//     // });

//     let index1 = convert_list_to_map(list, |item|{
//         item.code.clone()
//     });

//     println!("index1 : {:?}",index1);

// }